# coding: utf-8
"""
Baidu Channel
"""
import time
import uuid
import urllib
import urllib2

try:
    import json
except ImportError:
    import simplejson as json

CHANNEL_HOST = 'channel.api.duapp.com'
CHANNEL_DOMAIN = 'https://channel.api.duapp.com/'

HTTP_STATUS_OK = 200
HTTP_GET = 0
HTTP_POST = 1


class ChannelException(Exception):
    pass


class ChannelHttpException(ChannelException):
    """
    Base Exception
    """

    def __init__(self, code, data):
        try:
            _data = json.loads(data)
        except:
            _data = {}
        self.code = code
        self.error = _data.get("error_code", 'Unknown')
        self.error_description = _data.get("error_msg", data)
        self.data = _data
        super(ChannelHttpException, self).__init__(str(self))

    def __str__(self):
        return "%s %s(%s): %s" % (self.code, self.error, self.error_description, self.data)


class FancyDict(dict):
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError, k:
            raise AttributeError, k

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, key):
        try:
            del self[key]
        except KeyError, k:
            raise AttributeError, k


def assert_list(obj):
    return obj if isinstance(obj, (list, tuple)) else [obj]


PUSH_TYPES = FancyDict()
PUSH_TYPES.USER = 1
PUSH_TYPES.TAG = 2
PUSH_TYPES.ALL = 3


def http_request(url, params={}, method=HTTP_GET, ret_type="json"):
    """
    Send a request through urllib2
    """
    data = urllib.urlencode(params) if params else None
    if method == HTTP_GET:
        url = '%s?%s' % (url, urllib.urlencode(params))
        data = None
    try:
        request = urllib2.Request(url, data)
        ret = urllib2.urlopen(request)
    except urllib2.HTTPError, e:
        ret = e

    # Raise Exception if got an error status
    if ret.code != HTTP_STATUS_OK:
        raise ChannelHttpException(ret.code, ret.read())

    ret = ret.read()
    if ret_type == "json":
        ret = FancyDict(json.loads(ret))
    return ret


def get_access_token(api_key, api_secret):
    """
    Get access token from baidu, need api_key and api_secret
    """
    url = "https://openapi.baidu.com/oauth/2.0/token"
    ret = http_request(url, params={
        'grant_type': 'client_credentials',
        'client_id': api_key,
        'client_secret': api_secret
    })
    return ret


class ChannelClient(object):
    """
    Client
    ======

    Init need at least two arguments: api_key、 api_secret
    """
    access_token = None
    expires_at = time.time()

    def __init__(self, api_key, api_secret, android_proxy_address=""):
        self.api_key = api_key
        self.api_secret = api_secret
        self.android_proxy_address = android_proxy_address

    def refresh_access_token(self):
        access_token = get_access_token(self.api_key, self.api_secret)
        ChannelClient.access_token = access_token.access_token
        ChannelClient.expires_at = access_token.expires_in + time.time()

    def _call(self, method, channel_id=None, params={}):
        """
        All use post method
        """
        if time.time() + 3600 > self.expires_at:
            print 'Requesting new access token...'
            self.refresh_access_token()

        channel_id = channel_id or 'channel'
        params.update(
            method=method,
            access_token=ChannelClient.access_token,
        )

        # Bugfix: Only update channel_id param when using a proxy
        if method == 'pushmsg_to_user' and self.android_proxy_address:
            params.update(channel_id=channel_id)
            return http_request('%s' % self.android_proxy_address, params=params,
                                method=HTTP_POST)
        else:
            return http_request('%srest/2.0/channel/%s' % (CHANNEL_DOMAIN, channel_id),
                                params=params,
                                method=HTTP_POST)

    def set_tag(self, tag, user_id):
        """
        设置用户标签
        """
        return self._call('set_tag', params={'tag': tag, 'user_id': user_id})

    def delete_tag(self, tag, user_id=None):
        """
        删除用户标签, 如果指定user_id，则为删除tag和该用户id之间的对应关系
        """
        return self._call('delete_tag', params={'tag': tag, 'user_id': user_id or ''})

    def query_user_tags(self, user_id):
        """
        查询用户所属的标签列表
        """
        return self._call('query_user_tags', params={'user_id': user_id})

    def create_group(self, name):
        """
        创建广播组
        """
        return self._call('create_group', params={"name": name})

    def destroy_group(self, gid):
        """
        删除广播组
        """
        return self._call('destroy_group', params={"gid": gid})

    def push_msg(self, messages, tag=None, user_id=None, channel_id=None):
        """
        [新] 发送推送消息
        """
        if tag:
            push_type = PUSH_TYPES.TAG
        elif user_id:
            push_type = PUSH_TYPES.USER
        else:
            raise ChannelException('Either tag or user_id must be given.')

        messages = assert_list(messages)
        message_keys = [uuid.uuid4().hex for x in messages]
        params = {
            'push_type': push_type,
            'messages': json.dumps(messages),
            'msg_keys': json.dumps(message_keys),
        }
        if tag:
            params.update(tag=tag)
        if user_id:
            params.update(user_id=user_id)
        return self._call('push_msg', channel_id=channel_id, params=params)

    def _base_pushmsg(self, method, user_id, messages, channel_id=None):
        """
        Base push messages
        """
        messages = assert_list(messages)
        message_keys = [uuid.uuid4().hex for x in messages]
        return self._call(method, channel_id=channel_id, params={
            'user_id': user_id,
            'messages': json.dumps(messages),
            'msg_keys': json.dumps(message_keys),
        })

    def pushmsg(self, user_id, messages, channel_id=None):
        """
        功能：推送单播消息
        """
        return self._base_pushmsg('pushmsg', user_id, messages, channel_id=channel_id)

    def pushmsg_to_user(self, user_id, messages):
        """
        向用户推送消息
        """
        return self._base_pushmsg('pushmsg_to_user', user_id, messages)

    def push_android_msg(self, user_id, messages, channel_id=None):
        """
        推送单播消息
        """
        return self._base_pushmsg('pushmsg_to_user', user_id, messages, channel_id=channel_id)


if __name__ == '__main__':
    API_KEY = "TbtUHb16ZMae7ReifiTwUHrh"
    API_SECRET = "CLkPwiPmlP8ik01TBdgAOasfTG2j0MHt"

    #    access_token = get_access_token(API_KEY, API_SECRET)
    #    print access_token

    client = ChannelClient(API_KEY, API_SECRET)

    # print client.push_msg('米菲兔1127 关注了你', user_id='1053662048005717395')
    #    print client.pushmsg_to_user('1053662048005717395', 'hello')
    print client.push_msg(messages='test', tag='system_ad_dev')

#    print client.query_device_type(channel_id='4243118054617975627')
#    print client.set_tag('system_ad', '1075198402021517599')
# for user_id in ('698705359602522999', '970585152751208735'):
# print client.query_user_tags(user_id)
#    print client.delete_tag('2system_ad', user_id='1075198402021517599')
#    print client.delete_tag('system_ad', user_id='8267926250')
#    print client.query_user_tags('1075198402021517599')

# request_count = 50
# st = time.time()
# for i in range(request_count):
#    last_st = time.time()
# print client.pushmsg_to_user('12345', '恭喜发财')
#    print client.set_tag('12345', 'system_ad')
# print client.pushmsg_to_user('1075198402021517599', '恭喜发财')
#    print 'speed: %s, this time: %s' % ((i + 1) / (time.time() - st), time.time() - last_st)

# print client.pushmsg_to_user('933984184964649923', '恭喜发财')
#    print client.pushmsg('933984184964649923', 'Hello', channel_id='4243118054617975627')
#    print client.create_group('test group')
#    client.push_android_msg('933984184964649923', 'Hello', channel_id='4243118054617975627')
#
# print http_request('http://www.zlovezl.cn/test', ret_type='html')
#
